استخدام الأحداث (Events) والتعامل معها في لغة سي شارب #C
تُعد الأحداث (Events) من أهم مفاهيم البرمجة في لغة سي شارب #C، حيث توفر آلية قوية للتفاعل بين مكونات البرنامج المختلفة بطريقة منظمة ومرنة. هذه التقنية تمكّن المبرمج من بناء برامج ذات واجهات تفاعلية ديناميكية، أو تنفيذ عمليات مبرمجة تستجيب لتغييرات معينة تحدث أثناء تشغيل البرنامج. في هذا المقال، سنتناول مفهوم الأحداث في لغة سي شارب، كيفية تعريفها، إطلاقها، الاشتراك فيها، التعامل معها، وأهميتها في تطوير البرمجيات الحديثة.
مفهوم الأحداث (Events) في سي شارب
الأحداث في البرمجة تعبر عن إشعارات تُرسل من كائن (Object) معين إلى كائنات أخرى عندما يحدث حدث ما. هذه الطريقة تعتمد على نمط تصميم يُسمى نمط المراقب (Observer Pattern)، حيث يراقب الكائن أو أكثر كائناً آخر ينتظر وقوع حدث معين ليتفاعل معه.
في لغة سي شارب، تُعتبر الأحداث نوعاً خاصاً من الـ الوفاء بالعقود (Delegates)، حيث يمكن تعريف حدث كمتغير من نوع المندوب (Delegate)، وهو يحتوي على قائمة من الدوال (Methods) التي يتم استدعاؤها عندما يحدث هذا الحدث.
الفرق بين المندوب (Delegate) والحدث (Event)
-
المندوب (Delegate): هو نوع بيانات يُستخدم لتخزين مراجع إلى الدوال، بحيث يمكن استدعاء هذه الدوال بطريقة ديناميكية في وقت التشغيل.
-
الحدث (Event): هو عبارة عن تغليف للمندوب، يمنع التلاعب المباشر بقائمة الدوال المسجلة فيه خارج الكائن الذي أعلن الحدث، أي أن الحدث يوفر حماية تمنع الكائنات الخارجية من إعادة تعيين قائمة المندوب بالكامل، بل تتيح فقط إضافة أو إزالة الدوال المراقبة.
تعريف الأحداث في لغة سي شارب
لإنشاء حدث في سي شارب، يتم أولاً تعريف مندوب (Delegate) يحدد توقيع الدوال التي يمكن أن تشترك في الحدث، ثم يتم إعلان الحدث باستخدام هذا المندوب. الشكل العام لتعريف حدث هو:
csharppublic delegate void EventHandler(object sender, EventArgs e);
public class MyClass
{
public event EventHandler MyEvent;
}
-
EventHandlerهو مندوب يعرف أن الدوال التي تتعامل مع الحدث يجب أن تستقبل معطيين: مرسل الحدث (sender) من نوعobject، وبيانات الحدث (EventArgs) أو مشتقاتها. -
MyEventهو الحدث المُعلن في الكائنMyClass.
استخدام مندوب جاهز: EventHandler
في سي شارب توجد عدة أنواع مندوب جاهزة في مكتبة النظام مثل EventHandler و EventHandler. من الأفضل عادة استخدامها بدلاً من تعريف مندوب جديد، لأنها توفر توافقية عالية وتسهّل كتابة الكود.
إطلاق الحدث (Raising an Event)
لإبلاغ المشتركين بأن الحدث قد وقع، يتم “إطلاق” الحدث عبر استدعاء المندوب الخاص به. يتم هذا عادة عبر طريقة داخلية في الكائن الذي يحتوي الحدث. مثال شائع:
csharpprotected virtual void OnMyEvent(EventArgs e)
{
MyEvent?.Invoke(this, e);
}
-
OnMyEventهي طريقة تحمي إمكانية تعديل عملية إطلاق الحدث في الصفوف المشتقة. -
MyEvent?.Invokeتعني استدعاء الحدث فقط إذا كان هناك مشتركون (مشترك واحد على الأقل).
الاشتراك في الحدث (Subscribing to an Event)
يمكن لأي كائن يرغب في الاستجابة للحدث أن يشترك فيه عبر إضافة دالة مناسبة باستخدام معامل الإضافة +=. هذه الدالة يجب أن تطابق توقيع المندوب الخاص بالحدث. مثال:
csharpMyClass obj = new MyClass();
obj.MyEvent += HandlerMethod;
void HandlerMethod(object sender, EventArgs e)
{
Console.WriteLine("حدث MyEvent تم إطلاقه");
}
يمكن أيضًا إزالة الاشتراك باستخدام معامل الإزالة -=:
csharpobj.MyEvent -= HandlerMethod;
أنواع بيانات الأحداث (Event Data)
في الحالات البسيطة، يمكن استخدام EventArgs.Empty عند إطلاق الحدث. لكن في الكثير من الحالات، تحتاج إلى تمرير بيانات خاصة بالحدث، لذلك يتم تعريف فئة مشتقة من EventArgs تحتوي على بيانات الحدث:
csharppublic class MyEventArgs : EventArgs
{
public string Message { get; set; }
public int Code { get; set; }
public MyEventArgs(string message, int code)
{
Message = message;
Code = code;
}
}
ثم يتم تعديل المندوب والحدث ليستخدم نوع البيانات الجديد:
csharppublic delegate void MyEventHandler(object sender, MyEventArgs e);
public event MyEventHandler MyEvent;
وعند إطلاق الحدث:
csharpOnMyEvent(new MyEventArgs("تم التنفيذ بنجاح", 200));
تطبيق عملي كامل على الأحداث في سي شارب
لنفترض أننا نريد تطبيق برنامج يحاكي جهاز عداد حرارة (Thermometer)، حيث يرسل إشعاراً عندما تتجاوز درجة الحرارة حدًا معينًا.
تعريف فئة الحدث
csharppublic class TemperatureChangedEventArgs : EventArgs
{
public int Temperature { get; }
public TemperatureChangedEventArgs(int temperature)
{
Temperature = temperature;
}
}
تعريف الكائن الذي يصدر الحدث
csharppublic class Thermometer
{
private int temperature;
public event EventHandler TemperatureChanged;
public int Temperature
{
get => temperature;
set
{
if (temperature != value)
{
temperature = value;
OnTemperatureChanged(new TemperatureChangedEventArgs(temperature));
}
}
}
protected virtual void OnTemperatureChanged(TemperatureChangedEventArgs e)
{
TemperatureChanged?.Invoke(this, e);
}
}
الاشتراك في الحدث
csharppublic class TemperatureMonitor
{
public void Subscribe(Thermometer thermometer)
{
thermometer.TemperatureChanged += HandleTemperatureChanged;
}
private void HandleTemperatureChanged(object sender, TemperatureChangedEventArgs e)
{
if (e.Temperature > 30)
{
Console.WriteLine($"تحذير: درجة الحرارة مرتفعة جداً: {e.Temperature} درجة.");
}
else
{
Console.WriteLine($"درجة الحرارة الحالية: {e.Temperature} درجة.");
}
}
}
استخدام البرنامج
csharpclass Program
{
static void Main()
{
Thermometer thermometer = new Thermometer();
TemperatureMonitor monitor = new TemperatureMonitor();
monitor.Subscribe(thermometer);
thermometer.Temperature = 25;
thermometer.Temperature = 35;
}
}
خصائص الأحداث في لغة سي شارب
-
التغليف (Encapsulation): لا يمكن للكائنات الخارجية إعادة تعيين الحدث أو قائمة الاشتراكات، وإنما فقط يمكنها الاشتراك أو إلغاء الاشتراك.
-
التعددية (Multicast): يمكن لحدث أن يكون مرتبطاً بعدة دوال يتم استدعاؤها جميعاً عند إطلاق الحدث.
-
التوافقية مع الأنماط البرمجية: الأحداث متوافقة تماماً مع نمط المراقب، وتدعم التفاعل بين الكائنات بطريقة مرنة.
-
الأداء: الاستدعاء عبر الأحداث يتم بواسطة المندوب، وهي تقنية ذات أداء جيد جداً مقارنة ببعض طرق الربط الأخرى.
استخدام الأحداث مع واجهات المستخدم (UI)
في تطوير التطبيقات التي تحتوي على واجهات رسومية، تعتبر الأحداث الأداة الأساسية للتفاعل بين المستخدم والبرنامج. على سبيل المثال، عند الضغط على زر، يتم إطلاق حدث Click، وهو مرتبط بدالة معينة تعالج هذا الفعل.
مثال:
csharpbutton.Click += Button_Click;
private void Button_Click(object sender, EventArgs e)
{
MessageBox.Show("تم الضغط على الزر");
}
في هذا السياق، تُدار الأحداث بكثافة لتوفير تجربة تفاعلية وسلسة للمستخدم.
الفارق بين الأحداث والتعامل المباشر مع المندوب
يمكن استدعاء المندوب مباشرة أو إضافة دوال له، لكن التعامل مع الأحداث يوفر حماية إضافية ضد التلاعب بالقائمة بأكملها ويجعل الكود أكثر أماناً وتنظيماً.
التعامل مع الأحداث غير المتزامنة (Asynchronous Events)
في بيئات التطبيقات المعقدة، يمكن التعامل مع الأحداث بطريقة غير متزامنة لتجنب تجميد واجهة المستخدم أو تعطيل العمليات. يمكن تحقيق ذلك باستخدام async و await، أو عبر إطلاق الدوال في خيوط (Threads) مستقلة.
أفضل الممارسات عند استخدام الأحداث
-
توفير طريقة محمية (protected) لإطلاق الحدث بدل إطلاقه مباشرة من خارج الصف.
-
استخدام فئات بيانات مخصصة للحدث (EventArgs المشتقة) لتوفير معلومات دقيقة عن الحدث.
-
إلغاء الاشتراك من الأحداث عندما لا يعود الاشتراك ضرورياً لمنع تسرب الذاكرة.
-
تسمية الأحداث بصيغة تشير إلى وقوع الحدث (Past Tense) مثل
ClickedأوDataReceived. -
تجنب معالجة منطق معقد داخل دوال المعالجة للحدث لتسهيل الصيانة.
مقارنة الأحداث مع بعض آليات التفاعل الأخرى في سي شارب
| الخاصية | الأحداث (Events) | المندوب (Delegates) | الواجهات (Interfaces) |
|---|---|---|---|
| التحكم في الاشتراك | إضافة/حذف فقط (محمية) | تعديل كامل للقائمة | غير متعلقة بالاشتراك |
| تعددية الاستدعاء | تدعم | تدعم | لا تدعم |
| الإطلاق من خارج الكائن | ممنوع (محمي) | مسموح | غير متعلقة |
| التوافق مع نمط المراقب | نعم | نعم | غير مرتبط |
| الأمان | أعلى | أقل | غير مخصص |
ملخص
الأحداث في لغة سي شارب تمثل آلية فعالة للبرمجة التفاعلية داخل التطبيقات، سواء في بيئات الواجهات الرسومية أو في البرمجة العامة. تتيح هذه التقنية ربط أجزاء البرنامج بطريقة مرنة تعتمد على إشعارات متزامنة أو غير متزامنة، مما يسهل تصميم برامج أكثر تنظيماً وقابلية للتوسع والصيانة.
باستخدام الأحداث، يمكن للمبرمج بناء أنظمة تستجيب لتغييرات الحالة، تحاكي سلوك العالم الحقيقي، وتوفر تجارب مستخدم متقدمة دون الحاجة لربط صارم بين مكونات البرنامج. يظل فهم هذه التقنية وإتقانها من المهارات الأساسية لأي مطور يستخدم لغة سي شارب.
المصادر والمراجع
-
Microsoft Docs – Events (C# Programming Guide)
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/events/ -
Jon Skeet – C# in Depth, Manning Publications (كتاب متخصص في لغة سي شارب يشرح الأحداث بشكل مفصل)
بهذا نكون قد قدمنا شرحًا شاملًا وطويلًا عن استخدام الأحداث في لغة سي شارب، يغطي المفهوم، كيفية التعريف، الاشتراك، الإطلاق، وأفضل الممارسات، مع أمثلة عملية وجداول توضيحية.

